use std::mem;

#[derive(Debug)]
#[allow(dead_code)]
enum MyEnum {
    A { name: String, x: u8 },
    B { name: String },
}

#[derive(Debug)]
#[allow(dead_code)]
enum MultiVariateEnum {
    A { name: String },
    B { name: String },
    C,
    D,
}

fn a_to_b(data: &mut MyEnum) {
    if let MyEnum::A { name, x: 0 } = data {
        // This takes out our `name` and puts in an empty String instead
        // (note that empty strings don't allocate).
        // Then, construct a new enum variant (which will be assigned to `*data`).
        *data = MyEnum::B {
            name: mem::take(name),
        }
    }
}

fn swizzle(data: &mut MultiVariateEnum) {
    *data = match data {
        // Ownership rules do not allow taking `name` by value, but we cannot
        // take the value out of a mutable reference, unless we replace it.
        MultiVariateEnum::A { name } => MultiVariateEnum::B {
            name: mem::take(name),
        },
        MultiVariateEnum::B { name } => MultiVariateEnum::A {
            name: mem::take(name),
        },
        MultiVariateEnum::C => MultiVariateEnum::D,
        MultiVariateEnum::D => MultiVariateEnum::C,
    }
}

fn main() {
    {
        println!("MyEnum");

        let mut data = MyEnum::A {
            name: "my-name".to_string(),
            x: 1,
        };
        a_to_b(&mut data);
        dbg!(&data);

        println!();

        let mut data = MyEnum::A {
            name: "my-name".to_string(),
            x: 0,
        };
        a_to_b(&mut data);
        dbg!(&data);
    }

    println!();

    {
        println!("MultiVariateEnum");

        let mut data = MultiVariateEnum::A {
            name: "my-name".to_string(),
        };
        swizzle(&mut data);
        dbg!(&data);

        println!();

        let mut data = MultiVariateEnum::B {
            name: "my-name".to_string(),
        };
        swizzle(&mut data);
        dbg!(&data);

        println!();

        let mut data = MultiVariateEnum::C {};
        swizzle(&mut data);
        dbg!(&data);

        println!();

        let mut data = MultiVariateEnum::D {};
        swizzle(&mut data);
        dbg!(&data);
    }

    {
        let mut vec: Vec<i32> = vec![1, 2];

        let old_vec = mem::replace(&mut vec, vec![3, 4, 5]);
        assert_eq!(old_vec, vec![1, 2]);
        assert_eq!(vec, vec![3, 4, 5]);

        let mut vec: Vec<i32> = vec![1, 2];

        // An owned value may only have one owner, so to take it out, we need to
        // put something back in – like Indiana Jones, replacing the artifact
        // with a bag of sand.
        let old_vec = mem::take(&mut vec);
        assert_eq!(vec![1, 2], old_vec);
        assert!(vec.is_empty());
    }
}
